#pragma once
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cstring>
#include <string>
#include <functional>
#include <unistd.h>
#include <pthread.h>
#include "Log.hpp"
#include "InetAddr.hpp"
#include "mythread.hpp"
#include "threadpool.hpp"

const int gsockfd = -1;
const u_int16_t gport = 8888;
const int gbacklog = 8;

using task_t = std::function<void()>;
enum
{
    SOCKET_ERROR = 1,
    BIND_ERROR,
    LISEN_ERROR
};
class TcpServer
{
public:
    TcpServer(uint16_t port = gport)
        : _port(port), _listensockfd(gsockfd), _isrunning(false)
    {
    }
    void Init()
    {
        // 创建tcp套接字
        _listensockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (_listensockfd < 0)
        {
            LOG(FATAL, "sockfd create fall!\n");
            exit(SOCKET_ERROR);
        }
        LOG(INFO, "create sockfd success,sockfd:%d\n", _listensockfd);
        // bind
        struct sockaddr_in local;
        local.sin_family = AF_INET;
        local.sin_port = htons(_port);
        local.sin_addr.s_addr = INADDR_ANY;
        int n = ::bind(_listensockfd, (struct sockaddr *)&local, sizeof(local));
        if (n < 0)
        {
            LOG(FATAL, "bind false!\n");
            exit(BIND_ERROR);
        }
        LOG(INFO, "bind success!\n");
        // listen
        n = ::listen(_listensockfd, gbacklog);
        if (n < 0)
        {
            LOG(FATAL, "listen false!\n");
            exit(LISEN_ERROR);
        }
        LOG(INFO, "listen success!\n");
    }
    void Service(int sockfd, InetAddr addr)
    {
        while (true)
        {
            char buffer[1024];
            int n = read(sockfd, buffer, sizeof(buffer) - 1);
            if (n > 0)
            {
                buffer[n] = 0;
                LOG(DEBUG, "[%s]#%s\n", addr.AddrStr().c_str(), buffer);
                std::string echo_message = "[tcpserver echo]#";
                echo_message += buffer;
                n = write(sockfd, echo_message.c_str(), sizeof(echo_message));
                if (n < 0)
                {
                    LOG(ERROR, "server write false!\n");
                }
            }
            else if (n == 0) // 读到文件结尾
            {
                LOG(INFO, "%s quit!\n", addr.AddrStr().c_str());
                break;
            }
            else
            {
                LOG(INFO, "read error!\n");
                break;
            }
        }
        sleep(1);
        ::close(sockfd);
    }

    static void *Excute(void *args)
    {
        pthread_detach(pthread_self());
        ServerData *sd = static_cast<ServerData *>(args);
        sd->_td->Service(sd->_sockfd, sd->_addr);
        delete sd;
        return nullptr;
    }

    class ServerData
    {
    public:
        ServerData(int sockfd, InetAddr &addr, TcpServer *td)
            : _sockfd(sockfd), _addr(addr), _td(td)
        {
        }

    public:
        int _sockfd;
        InetAddr _addr;
        TcpServer *_td;
    };

    void Start()
    {
        _isrunning = true;
        while (_isrunning)
        {
            struct sockaddr_in client;
            memset(&client, 0, sizeof(client));

            socklen_t len = sizeof(client);
            int sockfd = accept(_listensockfd, (struct sockaddr *)&client, &len);
            if (sockfd < 0)
            {
                LOG(ERROR, "accept false!\n");
                continue;
            }
            LOG(INFO, "connect a link! sockfd:%d\n", sockfd);
            InetAddr addr(client);
            // version1：有缺陷，Service是长服务，由于这个代码是单进程的，第二个客户端进来会执行不了
            // Service(sockfd, addr);

            // version2: 多进程
            // pid_t id = fork();
            // if (id == 0)
            // {
            //     // 子进程
            //     ::close(_listensockfd); // 防止误操作
            //     // 由于是阻塞等待，service不退出父进程就不会向后继续执行循环，简单的方式是signal(SIGCHLD,SIG_IGN);,
            //     // 也可以这样写，创建一个孙子进程并直接exit这样子进程就变成了孤儿进程交给bash处理，这样我们就不需要管子进程了
            //     if (fork() > 0)
            //         exit(0);
            //     Service(sockfd, addr);
            //     exit(0);
            // }
            // ::close(sockfd); // 父进程不关心执行什么任务
            // pid_t rid = waitpid(id, nullptr, 0);
            // if (rid > 0)
            // {
            //     LOG(INFO, "wait success!\n");
            // }

            // version 3:多线程版
            // pthread_t tid;
            // ServerData* sd=new ServerData(sockfd,addr,this);
            // pthread_create(&tid, nullptr, Excute, sd);

            // version4:线程池
            // ps:线程池不适合做长服务，因为线程池的线程数量也是有限的，如果客户端数量超过线程数量，再有客户端加进来也得不到服务
            task_t t = std::bind(&TcpServer::Service, this, sockfd, addr);
            ThreadPool<task_t>::GetInstance()->Enqueue(t);
        }
    }

private:
    int _listensockfd;
    uint16_t _port;
    bool _isrunning;
};